<?php

namespace Service\SearchService\Index;
use Service\SearchService\BaseElasticSearchClientService;
use Service\Code\Normal;
use Service\SearchService\SearchService;

/**
 * 店铺C端商品搜索
 * @author chenshubo
 */
class StoreCommodityIndex extends BaseElasticSearchClientService
{
    /**
     * 索引名字
     * @var string
     */
    public $index = 'store_commodity';
    
    /**
     * 商品索引结构
     * @var array
     */
    public $mapping_property = [
        'ID' => [
            'type'  => 'keyword',
        ],
        'StoreGoodsID' => [//排序使用
            'type'  => 'float',
        ],        
        'GoodsID' => [
            'type'  => 'keyword',
        ],
        'ClassID' => [
            'type'  => 'keyword',
        ],
        'StoreID' => [
            'type'  => 'keyword',
        ],
        'GoodsName' => [
            'type'      => 'text',
            'analyzer'  => 'ik_smart',
            'search_analyzer'   => 'ik_smart',
            'fields'    => [
                "keyword"=> [
                    "type"              => "text",
                    "analyzer"          => "ik_smart",
                    "search_analyzer"   => "ik_smart",
                    "index"             => true,
                    "store"             => true
                ],
                "suggest"=> [
                    "type"                          => "completion",
                    "analyzer"                      => "simple",
                    "preserve_separators"           => true,
                    "preserve_position_increments"  => true,
                    "max_input_length"              => 50
                ]
            ]
        ],
        'BrandName' => [
            'type'      => 'text',
            'analyzer'  => 'ik_smart',
            'search_analyzer'   => 'ik_smart',
            'fields'    => [
                "keyword"=> [
                    "type"              => "text",
                    "analyzer"          => "ik_smart",
                    "search_analyzer"   => "ik_smart",
                    "index"             => true,
                    "store"             => true
                ],
                "suggest"=> [
                    "type"                          => "completion",
                    "analyzer"                      => "simple",
                    "preserve_separators"           => true,
                    "preserve_position_increments"  => true,
                    "max_input_length"              => 50
                ]
            ]
        ],
    ];
    
    /**
     * 全量删除商品文档数据
     * @param array $body
     */
    public function deleteAllMultiDoc(array $param)
    {
        $cid = $param['datas'];
        $param = [
            'index' => $this->index,
             'body' => [
                'script'=> ['source'=>'ctx._source["IsChannelGoodsActive"]=false']
             ]
        ];
        $param['body']['query']['bool']['must'][] = ['match'=>['ChannelID'=>$cid]];
        $param['body']['query']['bool']['must'][] = ['match'=>['IsTrade'=>false]];
        $return = $this->updateByQuery($param);
        if( empty($return['task']) ) {
            return Normal::returnCode(Normal::FAIL,"批量修改索引失败",$return);
        }
        return Normal::returnCode(Normal::SUC,"批量修改索引任务已经提交异步任务执行,请耐心等待处理数据结果!",$return);
    }
    
    /**
     * 批量处理新增/更新文档
     * @param array $param
     * @return int[]|string[]|array[]
     */
    public function addMultiDoc(array $param)
    {
        $DataList = $param['datas'];
        if (empty($DataList)) {
            return Normal::returnCode(Normal::SUC, "数据为空，不需要消费");
        }
        $body = [];
        foreach ($DataList as $item) 
        {
            $SkuID = $item['id'];
            $body[] = [
                'index' => [
                    '_index' => $this->index,
                    '_id' => $SkuID
                ]
            ];
            $body[] = [
                'ID'                    => $SkuID,
                'GoodsID'               => $item['goodsId'],
                'GoodsName'             => $item['name'],
                'BrandName'             => $item['brandName'],
                'ClassID'               => $item['classifyID'],
                'StoreID'               => $item['storeID'],
                "SuggestStoreID"        => $item['storeID'].'_'.(string)(int)$item['isStoreGoodsActive'],
                "ChannelID"             => $item['channelID'] ?? 0,
                'IsStoreGoodsActive'    => $item['isStoreGoodsActive'],
                'IsChannelGoodsActive'  => $item['isChannelGoodsActive'],
                'IsTrade'               => (bool)$item['isTrade']
            ];
        }
        $res = $this->bulk($body);
        if(!$res){
            return Normal::returnCode(Normal::FAIL, "批量创建索引失败");
        }
        return Normal::returnCode(Normal::SUC, "批量创建索引成功");
    }

    /**
     * 批量删除处理文档
     */
    public function deleteMultiDoc(array $param)
    {
        if (!isset($param['datas'])) {
            return Normal::returnCode(Normal::FAIL, "数据datas不能为空");
        }
        $DataList = $param['datas'];
        if (empty($DataList)) {
            return Normal::returnCode(Normal::SUC, "数据为空，不需要消费");
        }
        $param = [
            'index' => $this->index,
            'body'  => [
                'query' => ['terms'=>['GoodsID'=>$DataList]],
                'script'=> ['source'=>'ctx._source.IsChannelGoodsActive=false'],
            ]
        ];
        $return = $this->updateByQuery($param);
        if( empty($return['task']) ) {
            return Normal::returnCode(Normal::FAIL, "批量修改索引失败",$return);
        }
        return Normal::returnCode(Normal::SUC, "批量修改索引任务已经提交异步任务执行,请耐心等待处理数据结果!",$return);
    }
    
    /**
     * 搜索建议搜索功能
     * @param array $param
     */
    public function searchSuggest(array $param)
    {
        $mn_sid     = $param['mn_sid'];
        $keyword    = $param['keyword'];
        $limit      = $param['limit'] ?? 5;
        //搜索商品名称
        $suggest = [
            'commoditySuggest'  => [
                'prefix'        => $keyword,
                'completion'    => [
                    'contexts'    => [
                        'SuggestStoreID'  => [
                            [
                                'context' => "{$mn_sid}_1"
                            ]
                        ]
                    ],
                    'fuzzy' => [
                        'fuzziness'     => 'AUTO',
                        'unicode_aware' => 'true',
                    ],
                    'skip_duplicates'   => true,
                    'field'             => 'GoodsName.suggest',
                    'size'              => $limit
                ]
            ]
        ];
        $request = [
            'index'     => $this->index,
            'body'      => [
                '_source'   => false,
                'suggest'   => $suggest,
            ],
        ];
        
        $result = $this->getAggsList($request);
        if( isset($result['suggest']['commoditySuggest']) ) 
        {
            $commoditySuggestResult = current($result['suggest']['commoditySuggest'])['options'];
            if( empty($commoditySuggestResult) )
            {
                return Normal::returnCode(Normal::SUC,"未找到当前店铺ID{$mn_sid}下搜索建议词汇【{$keyword}】数据",[
                    'word'  => []
                ]);
            }
            
            $dataList = array_column($commoditySuggestResult,'text');
            return Normal::returnCode(Normal::SUC,"查询数据成功",[
                'word'  => $dataList
            ]);
        }
        
        return Normal::returnCode(Normal::FAIL,"查询数据异常",$result);
    }
    
    /**
     * 搜索商品
     * @param array $param
     */
    public function search(array $param)
    {
        $query = $this->buildElasticSearch($param);
        if( empty($query) ) {
            $query['match_all'] =  new \stdClass();
        }
        
        $limit = (int)($param['limit'] ?? 10);
        $page  = (int)($param['page'] ?? 0);
        $page  = $page - 1 < 0 ? 0 : $page -1;

        //价格排序
        $sortRule = [];
        //ID正序
        $sortRule[]['_score']['order'] = 'desc';
        $sortRule[]['ID']['order'] = 'asc';
        $request = [
            'index'     => $this->index,
            'body'      => [
                'query'     => $query,
                'size'      => $limit,
                'from'      => $page * $limit
            ],
        ];
        if( !empty($sortRule) ) {
           $request['body']['sort'] = $sortRule;
        }
        
        
        $return = $this->getAggsList($request);
        if( isset($return['hits']['hits']) ) {
            $hits = $return['hits']['hits'];
            $result = [];
            foreach ($hits as $item) {
                $result[] = $item['_source']['GoodsID'];
            }
            return Normal::returnCode(Normal::SUC,"搜索完成",$result);
        } else {
            return Normal::returnCode(Normal::FAIL,"搜索异常",$return);
        }
    }

    /**
     * 搜索商品品牌
     * @param array $param
     */
    public function searchBrand(array $param)
    {
        $query = $this->buildElasticSearch($param);
        if( empty($query) ) {
            $query['match_all'] =  new \stdClass();
        }
        $request = [
            'index'     => $this->index,
            'body'      => [
                'query'     => $query,
                'size'      => 0,
                'aggs'      => [
                    'unique_brands' => [
                        'terms' => [
                            'field' => 'BrandName',
                            'size'  =>  10000
                        ]
                    ]
                ]
            ],
        ];

        $return = $this->getAggsList($request);

        if( isset($return['aggregations']['unique_brands']['buckets']) ) {
            $hits = $return['aggregations']['unique_brands']['buckets'];
            $result = [];
            foreach ($hits as $item) {
                $result[] = $item['key'];
            }
            return Normal::returnCode(Normal::SUC,"搜索完成",$result);
        } else {
            return Normal::returnCode(Normal::FAIL,"搜索异常",$return);
        }
    }

    /**
     * 根据店铺ID删除文档
     * @param array $param
     * @return void
     */
    public function deleteMultiDocByStoreID(array $param)
    {
        if (!isset($param['datas'])) {
            return Normal::returnCode(Normal::FAIL, "数据datas不能为空");
        }
        $sotreID = $param['datas'];
        if (empty($sotreID)) {
            return Normal::returnCode(Normal::SUC, "数据为空，不需要消费");
        }
        $param = [
            'index' => $this->index,
            'body'  => [
                'query' => ['term'=>['StoreID'=>$sotreID]],
            ]
        ];
        $return = $this->deleteByQuery($param);
        if( !isset($return['took']) ) {
            return Normal::returnCode(Normal::FAIL, "批量删除文档失败",$return);
        }
        return Normal::returnCode(Normal::SUC, "批量删除索引任务已经提交异步任务执行,请耐心等待处理数据结果!",$return);
    }
    

     /**
     * 构建搜索参数
     * @param array $param
     * @return \stdClass[]|unknown[][]
     */
    public function buildElasticSearch(array $param)
    {
        //搜索商品名称
        $body = [];
        $body['bool']['must'][] = ['term'=>['StoreID'=>$param['mn_sid']]];
        $body['bool']['must'][] = ['term'=>['IsStoreGoodsActive'=>true]];
        $body['bool']['must'][] = ['term'=>['IsChannelGoodsActive'=>true]];
        if( !empty($param['keyword']) )
        {
            $keyword = SearchService::instance()->getKeyword($param['keyword']);
            $shoulds_where[] = [
                'match' => [
                    'GoodsName' => [
                        'analyzer'  => 'standard',
                        'query'     => $keyword,
                        'operator'  => 'and',
                        'boost'     => 2.5
                    ]
                ]
            ];
            $shoulds_where[] = [
                'multi_match' => [
                    'analyzer'  => 'standard',
                    'fields'    => ['GoodsName'],
                    'query'     => $keyword,
                    'boost'     => 2.0
                ]
            ];
            $shoulds_where[] = [
                'wildcard' => [
                    'GoodsName' => [
                        'value'     => "*{$keyword}*",
                        'boost'     => 1.5
                    ]
                ]
            ];            
            $shoulds_where[] = [
                'fuzzy' => [
                    'GoodsName' => [
                        'value'     => $keyword,
                        'boost'     => 1.0
                    ]
                ]
            ];
            $shoulds_where[] = [
                'match_phrase' => [
                    'GoodsName' => $keyword
                ]
            ];            
            $body['bool']['must'][] = ['bool'=>['should'=>$shoulds_where]];
        }
        
        //搜素品牌名称
        if( !empty($param['brand_name']) )
        {
            $keyword = $param['brand_name'];
//            $body['bool']['must'][] = ['match'=>['BrandName'=>[
//                'query'     => $keyword,
//                'operator'  => 'and'
//            ]]];
            $body['bool']['must'][] = ['term'=>['BrandName'=>$keyword]];
        }
        
        //搜索分类ID
        if( !empty($param['class_id']) )
        {
            $class_ids = $param['class_id'];
            $class_ids = array_filter(array_unique(\explode(",",$class_ids)));
            $body['bool']['must'][] = ['terms'=>['ClassID'=>$class_ids]];
        }

        //如果没有任何搜索条件
        if( empty($body) ) {
            $body['match_all'] = new \stdClass();
        }
        
        return $body;
    }

    /**
     * 构建搜索参数
     * @param array $param
     * @return \stdClass[]|unknown[][]
     */
    public function buildElasticSearchOld(array $param)
    {
        //搜索商品名称
        $body = [];
        $body['bool']['filter'][] = ['term'=>['StoreID'=>$param['mn_sid']]];
        $body['bool']['filter'][] = ['term'=>['IsStoreGoodsActive'=>true]];
        $body['bool']['filter'][] = ['term'=>['IsChannelGoodsActive'=>true]];
        if( !empty($param['keyword']) )
        {
            $keyword = $param['keyword'];
            $body['bool']['must']['bool']['should'][] = ['match'=>['GoodsName' => ['query'=> $keyword,'operator'=>'and']]];
            $body['bool']['must']['bool']['should'][] = ['wildcard'=>['GoodsName' => ['value'=> "*{$keyword}*"]]];
            $body['bool']['must']['bool']['should'][] = ['match'=>['GoodsName.keyword' => ['query'=> $keyword,'operator'=>'and']]];
        }
        
        //搜素品牌名称
        if( !empty($param['brand_name']) )
        {
            $keyword = $param['brand_name'];
            $body['bool']['must'][] = ['match'=>['BrandName'=>[
                'query'     => $keyword,
                'operator'  => 'and'
            ]]];
        }
        
        //搜索分类ID
        if( !empty($param['class_id']) )
        {
            $class_ids = $param['class_id'];
            $class_ids = array_filter(array_unique(\explode(",",$class_ids)));
            $body['bool']['must'][] = ['terms'=>['ClassID'=>$class_ids]];
        }

        //如果没有任何搜索条件
        if( empty($body) ) {
            $body['match_all'] = new \stdClass();
        }
        
        return $body;
    }
    
    //DONNET老方法,暂时保留
    public function searchOldDotNetKeyWord($keyword)
    {
        $body['bool']['should'][] = [
            'multi_match'    => [
                'analyzer'   => 'standard',
                'fields'     => 'GoodsName',
                'query'      => $keyword
            ]
        ];
        $body['bool']['should'][] = [
            'wildcard'    => [
                'GoodsName'   => [
                    'value'  => "*{$keyword}*"
                    ]
                    ]
                    ];
        $body['bool']['should'][] = [
            'fuzzy'    => [
                'GoodsName'   => [
                    'value'  => $keyword
                ]
            ]
        ];
    }
    
    /**
     * 创建文档索引
     */
    public function putMapping(array $param)
    {
        $isExists = $this->existsIndices();
        if( !$isExists ) {
            return $this->createMapping();
        } else {
            return Normal::returnCode(Normal::SUC,"索引已存在",$this->getMapping());
        }
    }
}